package ip

import (
	"fmt"
	"net"
	"strings"
)

// GetPublicIpv6 获取系统的公网IPv6地址。
//
// 返回值:
//
// - []net.IP: 包含公网IPv6地址的切片。
//
// - error: 如果获取失败，则返回错误信息。
func GetPublicIpv6() ([]net.IP, error) {
	// 调用GetIpv6()函数获取所有IPv6地址。
	ipv6, err := GetIpv6()
	if err != nil {
		return nil, err
	}
	// 判断获取的IPv6地址是否为空，如果为空则返回错误。
	if len(ipv6) == 0 {
		return nil, fmt.Errorf("未找到IPv6地址")
	}
	var res []net.IP
	// 筛选属于公网的IPv6地址。
	for _, ip := range ipv6 {
		// 判断IPv6地址是否以"240"开头，如果是，则认为是公网地址。
		if strings.HasPrefix(ip.String(), "240") {
			res = append(res, ip)
		}
	}
	// 如果没有找到公网IPv6地址，则返回错误。
	if len(res) == 0 {
		return nil, fmt.Errorf("未找到公网IPv6地址")
	}
	// 返回筛选出的公网IPv6地址。
	return res, nil
}

// GetIpv6 获取系统中所有的非回环、非链路本地和非临时的IPv6地址
//
// 返回值:
//
// - []net.IP: 包含符合条件的IPv6地址的切片
//
// - error: 如果没有找到符合条件的IPv6地址，则返回错误
func GetIpv6() ([]net.IP, error) {
	// 初始化一个空的IP切片，用于存储符合条件的IPv6地址
	var ips []net.IP

	// 获取所有网络接口
	NetInterface, err := net.Interfaces()
	if err != nil {
		// 如果获取网络接口失败，返回nil和错误信息
		return nil, err
	}

	// 遍历每个网络接口
	for _, iface := range NetInterface {
		// 获取接口的地址列表
		addrList, err := iface.Addrs()
		if err != nil {
			// 如果获取地址列表失败，跳过当前接口
			continue
		}

		// 遍历每个地址
		for _, addr := range addrList {
			// 将地址转换为IPNet类型
			ipnet, ok := addr.(*net.IPNet)
			if !ok {
				// 如果转换失败，跳过当前地址
				continue
			}

			ip := ipnet.IP
			// 检查是否为IPv6地址，并且不是回环地址
			if ip.To16() != nil && !ip.IsLoopback() && strings.Contains(ip.String(), ":") {
				// 过滤掉链路本地地址和临时地址
				if !ip.IsLinkLocalUnicast() && !strings.HasPrefix(ip.String(), "2001:db8::") {
					// 将符合条件的IPv6地址添加到切片中
					ips = append(ips, ip)
				}
			}
		}
	}
	// 如果没有找到符合条件的IPv6地址，返回错误
	if len(ips) == 0 {
		return nil, fmt.Errorf("未找到IPv6地址")
	}

	// 返回符合条件的IPv6地址切片和nil错误
	return ips, nil
}

// GetPublicIpv6Eth 获取指定网卡的公共IPv6地址。
// 该函数首先调用GetIpv6Eth来获取指定网卡的所有IPv6地址，
// 然后筛选出以"240"开头的IPv6地址，将其作为结果返回。
// 参数:
//
//	eth - 网卡名称，例如"eth0"。
//
// 返回值:
//
//	[]net.IP - 筛选后的公共IPv6地址列表。
//	error - 如果获取IPv6地址时发生错误，则返回该错误。
func GetPublicIpv6Eth(eth string) ([]net.IP, error) {
	// 获取指定网卡的所有IPv6地址。
	ips, err := GetIpv6Eth(eth)
	if err != nil {
		// 如果发生错误，直接返回nil和错误。
		return nil, err
	}
	// 初始化结果变量。
	var res []net.IP
	// 遍历所有获取到的IPv6地址。
	for _, ip := range ips {
		// 筛选以"240"开头的IPv6地址。
		if strings.HasPrefix(ip.String(), "240") {
			// 将符合条件的IPv6地址添加到结果列表中。
			res = append(res, ip)
		}
	}
	// 返回筛选后的公共IPv6地址列表。
	return res, nil
}

// GetIpv6Eth 获取指定网卡的IPv6地址(包含私有地址)
// 该函数遍历所有网络接口，寻找与指定网卡名称匹配的接口，并尝试获取其IPv6地址。
// 参数:
//
//	eth: 网卡名称，例如 "eth0"。
//
// 返回值:
//
//	net.IP: 网卡的IPv6地址。
//	error: 如果找不到指定网卡或获取IPv6地址失败，则返回错误。
func GetIpv6Eth(eth string) ([]net.IP, error) {
	var ips []net.IP
	// 获取系统中的所有网络接口。
	NetInterface, err := net.Interfaces()
	if err != nil {
		return nil, err
	}

	// 遍历所有网络接口，寻找指定的网卡。
	for _, iface := range NetInterface {
		if iface.Name == eth {
			// 获取指定网卡的所有地址。
			addrList, err := iface.Addrs()
			if err != nil {
				return nil, err
			}

			// 遍历网卡地址，寻找IPv6地址。
			for _, addr := range addrList {
				if ipnet, ok := addr.(*net.IPNet); ok {
					//logger.Info(ipnet.IP)
					// 检查地址是否为IPv6且非回环地址。
					if ipnet.IP.To4() == nil && !ipnet.IP.IsLoopback() {
						// 判断是否属于公网地址(240开头的地址属于公网地址)
						if strings.HasPrefix(ipnet.IP.String(), "240") {
							ips = append(ips, ipnet.IP)
						}
					}
				}
			}
		}
	}

	// 如果没有找到IPv6地址，则返回错误。
	if len(ips) == 0 {
		return nil, fmt.Errorf("未找到IPv6地址或网卡不存在")
	}
	return ips, nil
}
